package com.ht.aems.fesservice;

import com.ht.aems.communicate.*;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.*;

/**
 * class PortManager
 * <p> class for managing ITransmitPort <br>
 * @author Dong Shufeng
 * date: 2006-8-6
 */
public class PortManager implements IPortManager {

    private static Logger log = Logger.getLogger(PortManager.class);

    private List<IPortManager> portManagers = new Vector<IPortManager>();

    private static PortManager instance;

    public static PortManager getInstance() {
        if (instance == null)
            instance = new PortManager();
        return instance;
    }

    private PortManager() {
        portManagers.add(new TcpClientManager());
        portManagers.add(new UdpPortManager());
    }

    public boolean startComm(IPortInfo portInfo) {//todo new type of portmanager is added this method should be modified
        if (portInfo == null || portInfo.getPortType() == null) return false;
        String portType = portInfo.getPortType();
        if (portType.equals(IPortInfo.COMM_TCP)
                && ((TcpPortInfo) portInfo).getMode() == TcpPortInfo.MODE_SERVDER) {
            int serverPort = ((TcpPortInfo) portInfo).getServerPort();
            if (!containsTcpServer(serverPort)) {
                TcpServer server;
                String className = ((TcpPortInfo) portInfo).getServerClass();
                if(className == null) //default server is used if no tcp server class is set
                    className = TcpServer.class.getName();
                if (className != null && !className.trim().equals("")) {
                    try {
                        if (((TcpPortInfo) portInfo).getRemoteIp().equals(TcpServer.ONE_CLIENT_ONLY_KEY))
                            server = (TcpServer) Class.forName(className).getConstructor(Integer.TYPE, Integer.TYPE).newInstance(serverPort, TcpServer.ONE_CLIENT_ONLY_MODE);
                        else
                            server = (TcpServer) Class.forName(className).getConstructor(Integer.TYPE, Integer.TYPE).newInstance(serverPort, TcpServer.MULTI_CLIENT_MODE);
                    } catch (Exception e) {
                        log.warn("Exception occured when trying get new instance of " + className + ":" + Arrays.toString(e.getStackTrace()));
                        return false;
                    }
                } else {
                    if (((TcpPortInfo) portInfo).getRemoteIp().equals(TcpServer.ONE_CLIENT_ONLY_KEY))
                        server = new TcpServer(serverPort, TcpServer.ONE_CLIENT_ONLY_MODE);
                    else
                        server = new TcpServer(serverPort, TcpServer.MULTI_CLIENT_MODE);
                }
                server.startServer();
                portManagers.add(server);
            }
        } else if (portType.equals(IPortInfo.COMM_TCP) && ((TcpPortInfo) portInfo).getMode() == TcpPortInfo.MODE_CLIENT) {
            String serverIp = ((TcpPortInfo) portInfo).getRemoteIp();
            int serverPort = ((TcpPortInfo) portInfo).getServerPort();
            if (!containsPort(portInfo))
                try {
                    Socket socket = new Socket();
                    socket.connect(new InetSocketAddress(serverIp, serverPort), 10000);
                    TcpTransmitPort tcpTransmitPort = new TcpTransmitPort(socket);
                    ((TcpClientManager) portManagers.get(0)).put((TcpPortInfo) portInfo, tcpTransmitPort);
                } catch (IOException e) {
                    log.warn(e);
                    return false;
                }
        }  else if (portType.equals(IPortInfo.COMM_UDP)) {
            //String serverIp = ((UdpPortInfo) portInfo).getRemoteAddress();
            if (!containsPort(portInfo)) {
                UdpTransmitPort port = new UdpTransmitPort((UdpPortInfo) portInfo);
                ((UdpPortManager) portManagers.get(1)).put((UdpPortInfo) portInfo, port);
            }
        }
        ITransmitPort port = getTransPort(portInfo);
        return port != null && port.openPort();
    }

    public ITransmitPort getTransPort(IPortInfo portInfo) {
        for (IPortManager portManager : portManagers) {
            ITransmitPort port = portManager.getTransPort(portInfo);
            if (port != null)
                return port;
        }
        return null;
    }

    public boolean containsTcpServer(int serverPort) {
        for (IPortManager portManager : portManagers) {
            if (portManager != null && portManager instanceof TcpServer) {
                if (((TcpServer) portManager).getServerPort() == serverPort)
                    return true;
            }
        }
        return false;
    }

    public boolean containsUdpServer(int serverPort) {
        for (IPortManager portManager : portManagers) {
            if (portManager != null && portManager instanceof TcpServer) {
                if (((TcpServer) portManager).getServerPort() == serverPort)
                    return true;
            }
        }
        return false;
    }

    public ITransmitPort removePort(IPortInfo portInfo) {
        for (IPortManager portManager : portManagers) {
            ITransmitPort port = portManager.removePort(portInfo);
            if (port != null)
                return port;
        }
        return null;
    }

    public void removePort(ITransmitPort port) {
        for (IPortManager portManager : portManagers) {
            portManager.removePort(port);
        }
    }

    public boolean containsPort(IPortInfo portInfo) {
        for (IPortManager portManager : portManagers) {
            if (portManager.containsPort(portInfo))
                return true;
        }
        return false;
    }

    public boolean containsPort(ITransmitPort port) {
        for (IPortManager portManager : portManagers) {
            if (portManager.containsPort(port))
                return true;
        }
        return false;
    }

    public void stop() {
        for (IPortManager portManager : portManagers) {
            if (portManager != null && portManager instanceof TcpServer)
                ((TcpServer) portManager).stopServer();
            else {
                assert portManager != null;
                for (ITransmitPort p : portManager.getAllPorts().values())
                    p.stopPort();
            }
        }
    }

    public int getConnectedNum() {
        int counter = 0;
        for (IPortManager portManager : portManagers) {
            if (portManager != null)
                counter += portManager.getAllPorts().size();
        }
        return counter;
    }

    public Map<String, ITransmitPort> getAllPorts() {
        Map<String, ITransmitPort> m = new HashMap<String, ITransmitPort>();
        for (IPortManager portManager : portManagers) {
            if (portManager != null)
                m.putAll(portManager.getAllPorts());
        }
        return m;
    }
}
